package com.web.selenium;

import java.io.File;

import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;

import com.app.AppContext;
import com.web.model.PageBrowser;
import com.web.model.PageFactory;
import com.web.model.ScreenShot;
import com.web.model.WebPage;
import com.web.model.element.ElementFactory;

public class SeleniumWebBrowser implements PageBrowser{
	
	private final ElementFactory elementFactory = new SeleniumElementFactory(this);
	
	private final PageFactory pageFactory = AppContext.getBean(PageFactory.class);
	
	private int maxSecondsToWait;
	
	private WebDriver webDriver;
	
	private boolean isAlive;
	
	public SeleniumWebBrowser(WebDriver driver, int maxSecondsToWait){
		
		this.webDriver = driver;
		this.maxSecondsToWait = maxSecondsToWait;
		
		this.isAlive = true;
	}
	
	public <T extends WebPage> T open(Class<T> pageClass) {
		
		T page = pageFactory.newPage(pageClass, this);
		
		checkBrowserAlive();
		
		String pageUrl = page.getUrl();
			
		webDriver.get(pageUrl);
			
		elementFactory.initElements(page);
		
		return page;
	}
	
	public void checkBrowserAlive(){
		
		if(!isAlive){
		
			throw new IllegalStateException("Browser is not alive!");
		}	
	}
	
	public <T extends WebPage> T goTo(Class<T> pageClass) {
		
		T page = pageFactory.newPage(pageClass, this);
		
		checkBrowserAlive();
		
		elementFactory.initElements(page);
		
		return page;
	}
	
	public void quit() {
		webDriver.quit();
		this.isAlive = false;
	}
	
	public void deleteCookies(){
		webDriver.manage().deleteAllCookies();
	}
	
	public WebElement findWebElementById(String elementId){
		
		try{
		
			return findWebElement(By.id(elementId));
		}
		catch(TimeoutException e){
			
			throw new IllegalStateException(String.format("Browser cannot find element id '%s'", elementId), e);
		}
	}
	
	public WebElement findWebElementByClass(String className){
		
		try{
		
			return findWebElement(By.className(className));
		}
		catch(TimeoutException e){
			
			throw new IllegalStateException(String.format("Browser cannot find element by class '%s'", className), e);
		}
	}
	
	private WebElement findWebElement(By by) throws TimeoutException{
		
		WebElementWait elementWait = new WebElementWait(by);
		new WebDriverWait(webDriver, maxSecondsToWait).until(elementWait);

		return elementWait.getWebElement();	
	}
	
	public boolean isAlive(){
		return isAlive;
	}
	
	public ScreenShot takeScreenShot() {
		
		TakesScreenshot screenshotTaker = ((TakesScreenshot) webDriver);
		File screenshotFile = screenshotTaker.getScreenshotAs(OutputType.FILE);
		
		return new ScreenShot(screenshotFile);
	}

	public boolean isUrlMatching(final String pattern) {
		
		boolean urlMatches = true;
		ExpectedCondition<Boolean> urlMatching = new ExpectedCondition<Boolean>() {
			
			public Boolean apply(WebDriver driver) {
				return driver.getCurrentUrl().matches(pattern);
			}
		};
		try{
			new WebDriverWait(webDriver, maxSecondsToWait).until(urlMatching);
		}
		catch(TimeoutException e){
			
			urlMatches = false;
		}
		return urlMatches;
	}
	
	public boolean isTitlePresent(final String title) {
	
		boolean titlePresent = true;
		ExpectedCondition<Boolean> titleMatches = new ExpectedCondition<Boolean>() {
			
			public Boolean apply(WebDriver driver) {
				return driver.getTitle().equals(title);
			}
		};
		try{
			new WebDriverWait(webDriver, maxSecondsToWait).until(titleMatches);
		}
		catch(TimeoutException e){
			
			titlePresent = false;
		}
		return titlePresent;
	}
	
	
	public boolean isTextPresent(final String text) {
		
		boolean textPresent = true;
		ExpectedCondition<Boolean> textMatches = new ExpectedCondition<Boolean>() {
			
			public Boolean apply(WebDriver driver) {
				return driver.getPageSource().contains(text);
			}
		};
		try{
			new WebDriverWait(webDriver, maxSecondsToWait).until(textMatches);
		}
		catch(TimeoutException e){
			
			textPresent = false;
		}
		return textPresent;
	}

	public void executeScript(String script) {
		JavascriptExecutor je = (JavascriptExecutor) webDriver;
		je.executeScript(script);
	}
}
